#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#define PCI_VENDOR_ID_MYDEVICE 0x1234
#define PCI_DEVICE_ID_MYDEVICE 0x5678

static struct pci_device_id mydevice_pci_ids[] = {
    { PCI_DEVICE(PCI_VENDOR_ID_MYDEVICE, PCI_DEVICE_ID_MYDEVICE) },
    { 0, }
};
MODULE_DEVICE_TABLE(pci, mydevice_pci_ids);

static irqreturn_t mydevice_interrupt_handler(int irq, void *dev_id)
{
    // 在这里处理中断
    // 读取设备寄存器、处理数据等

    return IRQ_HANDLED;
}

static int mydevice_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    // nr_io_queues具体中断号的数量
    int nr_io_queues = 1U;
    int err = 0, i = 0, vecs = 0;
    unsigned long mmio_start = 0, mmio_len = 0;
    void __iomem *mmio_addr = NULL;
    // 数组包含MSI-X中断的配置信息
    struct msix_entry entry[5];

//    memset(entry, 0, sizeof(entry));

    // 获取设备的资源信息
    mmio_start = pci_resource_start(pdev, 0);
    mmio_len = pci_resource_len(pdev, 0);

    // 判断是否是内存资源
    if (pci_resource_end(pdev, 0) & IORESOURCE_MEM) {
        // 映射设备的内存地址
        mmio_addr = ioremap(mmio_start, mmio_len);
        if (!mmio_addr) {
            err = -ENOMEM;
            goto disable_device;
        }
    }

    /*
	 * If we enable msix early due to not intx, disable it again before
	 * setting up the full range we need.
	 */
	if (!pdev->irq) {
		pci_disable_msix(pdev);

    	for (i = 0; i < nr_io_queues; i++)
    		entry[i].entry = i;
    	vecs = pci_enable_msix_range(pdev, entry, 1, nr_io_queues);
    	if (vecs < 0) {
    		vecs = pci_enable_msi_range(pdev, 1, min(nr_io_queues, 32));
    		if (vecs < 0) {
    			vecs = 1;
    		} else {
    			for (i = 0; i < vecs; i++)
    				entry[i].vector = i + pdev->irq;
    		}
    	}
    } else {
        // 获取INTx中断号
        err = request_irq(pdev->irq, mydevice_interrupt_handler, IRQF_SHARED, "mydevice", pdev);
        if (err) {
            goto unmap_mmio;
        }
    }

    if (pci_enable_device_mem(pdev))
		return -ENOMEM;

	pci_set_master(pdev);
    // 启用PCI设备
//    err = pci_enable_device(pdev);
//    if (err)
//        return err;

    // 在这里执行其他必要的初始化操作

    return 0;

unmap_mmio:
    iounmap(mmio_addr);
disable_device:
    pci_disable_device(pdev);
    return err;
}

static void mydevice_remove(struct pci_dev *pdev)
{
    // 在这里执行驱动的清理操作

    free_irq(pdev->irq, pdev);
    iounmap(pci_resource_start(pdev, 0));
    pci_disable_device(pdev);
}

static struct pci_driver mydevice_pci_driver = {
    .name = "my_pic_device",
    .id_table = mydevice_pci_ids,
    .probe = mydevice_probe,
    .remove = mydevice_remove,
};

static int __init mydevice_init(void)
{
    return pci_register_driver(&mydevice_pci_driver);
}

static void __exit mydevice_exit(void)
{
    pci_unregister_driver(&mydevice_pci_driver);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

// 模块描述
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple PCI Driver Example with Interrupt Handling");
MODULE_LICENSE("GPL");

